// HSS.Forms.AppContext.cs
// ----------------------------------------------------------------------------
// Licensed under the MIT license
// http://www.opensource.org/licenses/mit-license.html
// ----------------------------------------------------------------------------
// HighSpeed-Solutions, LLC
// Copyright (c) 2001-2010
// ----------------------------------------------------------------------------
// File:       AppContext.cs
// Author:     HSS\gbanta
// Created:    08/12/2010
// Modified:   12/04/2010
// ----------------------------------------------------------------------------
namespace HSS.Forms
{
	#region Using Directives
	using System;
	using System.Drawing;
	using System.Reflection;
	using System.Security.Permissions;
	using System.Threading;
	using System.Windows.Forms;
	using System.Runtime.InteropServices;

	#endregion

	#region Enum
	/// <summary>
	/// When evaluating Application Settings, and determining if this is the
	/// first time the Application has been run, either first run on this machine
	/// or first run by the current user on this machine.
	/// </summary>
	public enum FirstRunScope
	{
		/// <summary>
		/// The current User
		/// </summary>
		User,
		/// <summary>
		/// The Machine
		/// </summary>
		Machine
	}
	#endregion

	#region AppContext
	/// <summary>
	/// A custom <see cref="System.Windows.Forms.ApplicationContext"/> with the use of Generics.
	/// </summary>
	/// <remarks>
	/// <para>
	/// The most notable feature of the AppContext class is its use of Generics. You specify the MainForm
	/// and optionally a SplashForm via Type Parameters. This allows for a robust implementation by providing the developer
	/// complete control over its use, yet the code to implement is a couple of lines.
	/// </para>
	/// <para>The <c>AppContext</c> class provides support for a number of WinForm features that would
	/// be considered standard for a Windows Forms Application.
	/// </para>
	/// <para>Feature List</para>
	/// <list type="bullet">
	/// <item>Multi-Threaded SplashScreen via the <see cref="ISplashScreen"/> interface</item>
	/// <item>Unified <c>Unhandled</c> and <c>Thread Exception</c> processing</item>
	/// <item>Unified <c>Exception</c> notification via the <see cref="AppExceptionDialog"/></item>
	/// <item><see cref="AppExceptionDialog"/> supports sending error information to the application's author
	/// if the <see cref="SendException"/> event has been implemented</item>
	/// <item>Implements persistence mechanism for saving and restoring the MainForm's location, size and state 
	/// via the <see cref="AppLocalUserData"/> class</item>
	/// </list>
	/// </remarks>
	/// <example>
	/// <para>
	/// The example below demonstrates using the AppContext class from the Main method of a Windows Application.
	/// </para>
	/// <code>
	/// using System;
	/// using System.Collections.Generic;
	/// using System.Text;
	/// using System.Windows.Forms;
	/// using HSS.Forms;
	/// 
	/// [STAThread()]
	/// static int Main(string[] args)
	/// {
	///		// You specify your Main Form and SplashScreen Form
	///		using (AppContext&lt;TestForm&gt; ctx = new AppContext&lt;TestForm&gt;)
	///			ctx.Run&lt;SomeSplashForm&gt;(args, initApp);
	///			
	///		// OR
	/// 
	///		// You specify just your MainForm
	///		//using (AppContext&lt;TestForm&gt; ctx = new AppContext&lt;TestForm&gt;)
	///		//	ctx.Run(args, initApp);
	/// 
	/// }
	/// 
	/// static TestForm initApp(string[] args, AppContext&lt;TestForm&gt; ctx)
	/// {
	/// 
	///		ctx.ShowError += new EventHandler&lt;SendExceptionEventArgs&gt;(App_ShowError);
	///		ctx.SendException += new EventHandler&lt;SendExceptionEventArgs&gt;(App_SendException);			
	///
	///		if (ctx.IsFirstUse(FirstRunScope.Machine))
	///			// Perform first run logic if this is the first run on this machine
	///
	///		if (ctx.IsFirstUse(FirstRunScope.User))
	///			// Perform first run logic if this is the first run for the current user on this machine 
	///
	///		ctx.UpdateSplash("loading app...", 10);
	///		// Do some work...
	///		
	///		ctx.UpdateSplash("loading test form...", 50);
	///		_testForm = new TestForm();
	///		
	///		ctx.UpdateSplash("initializing test form...", 70);
	///		if (_testForm.InitializeUI(ctx)) // optionally pass along ctx so the initUI can update the splashscreen
	///		{
	///			ctx.UpdateSplash("Test form ready...", 100);
	///			return _testForm;
	///		}
	///		
	///		// Return null; AppContext assumes cancel, and allows the app to close gracefully
	///		ctx.UpdateSplash("Test form failed to initialize!", 100);
	///		return null;
	/// }
	/// 
	/// /// <summary>
	/// /// The MainForm of the Application
	/// /// </summary>
	/// public static TestForm
	/// {
	///		get { return _testForm; }
	/// } static TestForm _testForm;
	/// </code>
	/// </example>
	/// <typeparam name="TForm">The MainForm of the Application</typeparam>
	[SecurityPermission(SecurityAction.Demand, ControlAppDomain = true)]
	public class AppContext<TForm> : ApplicationContext where TForm : Form
	{
		#region Delegates
		/// <summary>
		/// A delegate used to callback for performing updates of a SplashScreen.
		/// </summary>
		/// <param name="args">A string array containing any command line arguments</param>
		/// <param name="ctx">The AppContext currently executing</param>
		/// <returns>TForm - The MainForm instance for the AppContext</returns>
		public delegate TForm InitializeApp(string[] args, AppContext<TForm> ctx);
		#endregion

		#region Events
		/// <summary>
		/// Occurs when user clicks on "Send Error report" button if an Unhandled Exception occurs.
		/// </summary>
		public event EventHandler<SendExceptionEventArgs> SendException;
		/// <summary>
		/// Occurs when user clicks on "click here" link lable to see the data that will be sent if an Unhandled Exception occurs.
		/// </summary>
		public event EventHandler<SendExceptionEventArgs> ShowError;
		#endregion

		#region Fields
		private static AppContext<TForm> instance;
		InitializationException initException;
		AutoResetEvent uievent;
		AppData mainFormData;
		bool loading;
		#endregion

		#region Constructor
		/// <summary>
		/// Default constructor
		/// </summary>
		public AppContext()
		{
			instance = this;

			Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
			Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
			Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
			Application.EnableVisualStyles();
			Application.SetCompatibleTextRenderingDefault(false);

			AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

			uievent = new AutoResetEvent(false);
		}
		#endregion

		#region Properties
		/// <summary>
		/// Gets the Status Message displayed if a SplashForm is being shown
		/// </summary>
		public string StatusInfo
		{
			get { return _statusInfo; }
		} string _statusInfo;
		/// <summary>
		/// Gets the ProgressBar value if a SplashForm is being shown
		/// </summary>
		public int Progress
		{
			get { return _progress; }
		} int _progress;
		/// <summary>
		/// Gets or sets whether the <see cref="System.Windows.Forms.ApplicationContext.MainForm" />'s location (X and Y) should be restored.</summary>		
		/// <remarks>
		/// This property is true by default. The location will not be restored if doing so would 
		/// cause the form to fall outside the bounds of the available screen(s).</remarks>
		/// <seealso cref="RestoreSize" />
		/// <seealso cref="RestoreState" />
		public bool RestoreLocation
		{
			get
			{
				if (null == this.MainForm)
					return false;
				return mainFormData.Get("RestoreLocation", true);
			}
			set
			{
				if (null == this.MainForm)
					return;
				mainFormData.Set("RestoreLocation", value);
			}
		}
		/// <summary>
		/// Gets or sets whether the <see cref="System.Windows.Forms.ApplicationContext.MainForm" />'s size (width and height) should be restored.</summary>		
		/// <remarks>
		/// This property is true by default.</remarks>
		/// <seealso cref="RestoreLocation" />
		/// <seealso cref="RestoreState" />
		public bool RestoreSize
		{
			get
			{
				if (null == this.MainForm)
					return false;
				return mainFormData.Get("RestoreSize", true);
			}
			set
			{
				if (null == this.MainForm)
					return;
				mainFormData.Set("RestoreSize", value);
			}
		}
		/// <summary>
		/// Gets or sets whether the <see cref="System.Windows.Forms.ApplicationContext.MainForm" />'s state (normal, minimized, or maximized) 
		/// should be restored.</summary>		
		/// <remarks>
		/// This property is true by default. If the form state is minimized or maximized, its location
		/// and size will not be restored.</remarks>
		/// <seealso cref="RestoreLocation" />
		/// <seealso cref="RestoreSize" />
		public bool RestoreState
		{
			get
			{
				if (null == this.MainForm)
					return false;
				return mainFormData.Get("RestoreState", true);
			}
			set
			{
				if (null == this.MainForm)
					return;
				mainFormData.Set("RestoreState", value);
			}
		}
		/// <summary>
		/// Gets the Application's Assembly Title.
		/// </summary>
		/// <returns>string</returns>
		public string AssemblyTitle
		{
			get
			{
				if (string.IsNullOrEmpty(_assemblyTitle))
				{
					object[] attributes = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false);
					if (attributes.Length > 0)
					{
						AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0];
						if (!string.IsNullOrEmpty(titleAttribute.Title))
							_assemblyTitle = titleAttribute.Title;
					}
					if (string.IsNullOrEmpty(_assemblyTitle))
						_assemblyTitle = System.IO.Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().CodeBase);
				}
				return _assemblyTitle;
			}
		} string _assemblyTitle;
		/// <summary>
		/// Gets the Application's Assembly Version.
		/// </summary>
		/// <returns>string</returns>
		public string AssemblyVersion
		{
			get
			{
				if (string.IsNullOrEmpty(_assemblyVersion))
				{
					object[] attributes = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyFileVersionAttribute), false);
					if (attributes.Length > 0)
						_assemblyVersion = ((AssemblyFileVersionAttribute)attributes[0]).Version;
					else
						_assemblyVersion = Assembly.GetEntryAssembly().GetName().Version.ToString(4);
				}
				return _assemblyVersion;
			}
		} string _assemblyVersion;
		/// <summary>
		/// Gets the Application's Assembly Description.
		/// </summary>
		/// <returns>string</returns>
		public string AssemblyDescription
		{
			get
			{
				if (string.IsNullOrEmpty(_assemblyDescription))
				{
					object[] attributes = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false);
					if (attributes.Length > 0)
						_assemblyDescription = ((AssemblyDescriptionAttribute)attributes[0]).Description;
					else
						_assemblyDescription = "No description.";
				}
				return _assemblyDescription;
			}
		} string _assemblyDescription;
		/// <summary>
		/// Gets the Application's Assembly Product name.
		/// </summary>
		/// <returns>string</returns>
		public string AssemblyProduct
		{
			get
			{
				if (string.IsNullOrEmpty(_assemblyProduct))
				{
					object[] attributes = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false);
					if (attributes.Length > 0)
						_assemblyProduct = ((AssemblyProductAttribute)attributes[0]).Product;
					else
						_assemblyProduct = Assembly.GetEntryAssembly().GetName().Name;
				}
				return _assemblyProduct;
			}
		} string _assemblyProduct;
		/// <summary>
		/// Gets the Application's Assembly Copyright.
		/// </summary>
		/// <returns>string</returns>
		public string AssemblyCopyright
		{
			get
			{
				if (string.IsNullOrEmpty(_assemblyCopyright))
				{
					object[] attributes = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false);
					if (attributes.Length > 0)
						_assemblyCopyright = ((AssemblyCopyrightAttribute)attributes[0]).Copyright;
					else
						_assemblyCopyright = "(c) " + DateTime.Now.Year.ToString();
				}
				return _assemblyCopyright;
			}
		} string _assemblyCopyright;
		/// <summary>
		/// Gets the Application's Assembly Company Name.
		/// </summary>
		/// <returns>string</returns>
		public string AssemblyCompany
		{
			get
			{
				if (string.IsNullOrEmpty(_assemblyCompany))
				{
					object[] attributes = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);
					if (attributes.Length > 0)
						_assemblyCompany = ((AssemblyCompanyAttribute)attributes[0]).Company;
					else
						_assemblyCompany = "Missing company name.";
				}
				return _assemblyCompany;
			}
		} string _assemblyCompany;
		/// <summary>
		/// Gets the Application's Assembly Guid.
		/// </summary>
		/// <returns>string</returns>
		public string AssemblyGuid
		{
			get
			{
				if (string.IsNullOrEmpty(_assemblyGuid))
				{
					object[] attributes = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(GuidAttribute), false);
					if (attributes.Length > 0)
						_assemblyGuid = ((GuidAttribute)attributes[0]).Value;
					else
						_assemblyGuid = Guid.Empty.ToString();
				}
				return _assemblyGuid;
			}
		} string _assemblyGuid;
		/// <summary>
		/// Gets the Application's Assembly build configuration, such as retail or debug.
		/// </summary>
		/// <returns>string</returns>
		public string AssemblyConfiguration
		{
			get
			{
				if (string.IsNullOrEmpty(_assemblyConfiguration))
				{
					object[] attributes = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyConfigurationAttribute), false);
					if (attributes.Length > 0)
						return ((AssemblyConfigurationAttribute)attributes[0]).Configuration;
					else
						_assemblyConfiguration = "Unknown";
				}
				return _assemblyConfiguration;
			}
		} string _assemblyConfiguration;
		#endregion

		#region Methods

		/// <summary>
		/// Append a new line to the provided StringBuilder for each of the <see cref="System.Environment"/> properties.
		/// </summary>
		/// <param name="builder">The StringBuilder to append to.</param>
		/// <remarks>Each line appended contains the property name and value (ie, Name: Value)</remarks>
		public static void AppendEnvironmentInfo(System.Text.StringBuilder builder)
		{
			builder.AppendLine("*ENVIRONMENT*");
			builder.AppendLine();
			builder.AppendLine("CommandLine: " + Environment.CommandLine);
			builder.AppendLine("CurrentDirectory: " + Environment.CurrentDirectory);
			builder.AppendLine("HasShutdownStarted: " + Environment.HasShutdownStarted);
			builder.AppendLine("Is64BitOperatingSystem: " + Environment.Is64BitOperatingSystem);
			builder.AppendLine("Is64BitProcess: " + Environment.Is64BitProcess);
			builder.AppendLine("MachineName: " + Environment.MachineName);
			builder.AppendLine("OSVersion: " + Environment.OSVersion);
			builder.AppendLine("ProcessorCount: " + Environment.ProcessorCount);
			builder.AppendLine("StackTrace: " + Environment.StackTrace);
			builder.AppendLine("TickCount: " + Environment.TickCount);
			builder.AppendLine("UserDomainName: " + Environment.UserDomainName);
			builder.AppendLine("UserInteractive: " + Environment.UserInteractive);
			builder.AppendLine("UserName: " + Environment.UserName);
			builder.AppendLine("Version: " + Environment.Version);
			builder.AppendLine("WorkingSet: " + Environment.WorkingSet);
		}
		/// <summary>
		/// Append a new line to the provided StringBuilder for each of the <see cref="AppContext&lt;TForm&gt;"/> and <see cref="System.Windows.Forms.Application"/> properties.
		/// </summary>
		/// <param name="builder">The StringBuilder to append to.</param>
		/// <remarks>Each line appended contains the property name and value (ie, Name: Value)</remarks>
		public static void AppendAppContextInfo(System.Text.StringBuilder builder)
		{
			builder.AppendLine("*Application*");
			builder.AppendLine();
			builder.AppendLine("CommonAppDataPath: " + Application.CommonAppDataPath);
			builder.AppendLine("CompanyName: " + Application.CompanyName);
			builder.AppendLine("CurrentCulture: " + Application.CurrentCulture);
			builder.AppendLine("CurrentInputLanguage: " + Application.CurrentInputLanguage.Culture.ToString());
			builder.AppendLine("ExecutablePath: " + Application.ExecutablePath);
			builder.AppendLine("LocalUserAppDataPath: " + Application.LocalUserAppDataPath);
			builder.AppendLine("MessageLoop: " + Application.MessageLoop);
			builder.AppendLine("OpenForms.Count: " + Application.OpenForms.Count);
			builder.AppendLine("ProductName: " + Application.ProductName);
			builder.AppendLine("ProductVersion: " + Application.ProductVersion);
			builder.AppendLine("RenderWithVisualStyles: " + Application.RenderWithVisualStyles);
			builder.AppendLine("SafeTopLevelCaptionFormat: " + Application.SafeTopLevelCaptionFormat);
			builder.AppendLine("StartupPath: " + Application.StartupPath);
			builder.AppendLine("UserAppDataPath: " + Application.UserAppDataPath);
			builder.AppendLine("UseWaitCursor: " + Application.UseWaitCursor);
			builder.AppendLine("VisualStyleState: " + Application.VisualStyleState);

			builder.AppendLine();
			builder.AppendLine();
			builder.AppendLine("*AppContext<" + typeof(TForm).Name + ">*");
			builder.AppendLine();
			if (null != instance)
			{
				builder.AppendLine("AssemblyCompany: " + instance.AssemblyCompany);
				builder.AppendLine("AssemblyConfiguration: " + instance.AssemblyConfiguration);
				builder.AppendLine("AssemblyCopyright: " + instance.AssemblyCopyright);
				builder.AppendLine("AssemblyDescription: " + instance.AssemblyDescription);
				builder.AppendLine("AssemblyGuid: " + instance.AssemblyGuid);
				builder.AppendLine("AssemblyProduct: " + instance.AssemblyProduct);
				builder.AppendLine("AssemblyTitle: " + instance.AssemblyTitle);
				builder.AppendLine("AssemblyVersion: " + instance.AssemblyVersion);
			}
			else
			{
				builder.AppendLine("Instance has not been created.");
			}
		}

		/// <summary>
		/// Runs the Form TForm, showing the desired SplashScreen specified by TSplash
		/// </summary>
		/// <typeparam name="TSplash">The Type of the SplashScreen to show. Must implement ISplashScreen and inherit from From</typeparam>
		/// <param name="args">The command line args</param>
		/// <param name="appInitialize">The callback to perform the initialization</param>
		public void Run<TSplash>(string[] args, InitializeApp appInitialize) where TSplash : Form, ISplashScreen
		{
			this.Run<TSplash>(args, appInitialize, ApartmentState.STA);
		}
		/// <summary>
		/// Runs the Form TForm, showing the desired SplashScreen specified by TSplash
		/// </summary>
		/// <typeparam name="TSplash">The Type of the SplashScreen to show. Must implement ISplashScreen and inherit from From</typeparam>
		/// <param name="args">The command line args</param>
		/// <param name="appInitialize">The callback to perform the initialization</param>
		/// <param name="state">The desired ApartmentState for the thread that the SplashScreen will run on</param>
		public void Run<TSplash>(string[] args, InitializeApp appInitialize, ApartmentState state) where TSplash : Form, ISplashScreen
		{
			Thread th = new Thread(ShowThread<TSplash>);
			th.IsBackground = true;
			th.SetApartmentState(state);
			th.Start();

			// Wait for the SplashScreen to start
			if (!uievent.WaitOne(60000, false))
			{
				InitializationException iex = new InitializationException("Failed to load the SplashScreen in a timely fashion.");
				ShowAppExceptionDialog(iex);
				return;
			}

			// Ensure no errors
			if (null != initException)
			{
				ShowAppExceptionDialog(initException);
				return;
			}

			// Wait until the splash screen is actually visible
			while (!this.MainForm.Visible)
				Console.WriteLine("SplashScreen not visible yet...");

			// Now initialize the app calling the users code, receiving a reference to the MainForm
			TForm mainForm = null;
			try
			{
				mainForm = appInitialize(args, this);
			}
			catch (Exception initEx)
			{
				ShowAppExceptionDialog(initEx);
				return;
			}

			// Ensure we have a MainForm, assumes null is to cancel load
			if (null == mainForm)
			{
				this.Close();
				return;
			}

			// Done initializing, close the splash screen
			this.Close();

			// Wait for the splash screen to close and the current messageloop to end
			if (!uievent.WaitOne(60000, false))
			{
				InitializationException iex = new InitializationException("Failed to close the SplashScreen in a timely fashion.");
				ShowAppExceptionDialog(iex);
				return;
			}

			// Re-assign the real MainForm and Run the application...
			try
			{
				MessageService.MainForm = mainForm;
				this.mainFormData = AppLocalUserData.Get(mainForm.Name, new AppData());
				this.MainForm = mainForm;
				this.MainForm.Load += new EventHandler(HandleLoad);
				this.MainForm.FormClosed += new FormClosedEventHandler(HandleClosed);
				Application.Run(this);
			}
			catch (Exception initEx)
			{
				ShowAppExceptionDialog(initEx);
				return;
			}

		}
		/// <summary>
		/// Runs the Form TForm, without showing a SplashScreen
		/// </summary>
		/// <param name="args">The command line args</param>
		/// <param name="appInitialize">The callback to perform the initialization</param>
		public void Run(string[] args, InitializeApp appInitialize)
		{
			// Initialize the app calling the users code, receiving a reference to the MainForm
			TForm mainForm = null;
			try { mainForm = appInitialize(args, this); }
			catch (Exception initEx)
			{
				ShowAppExceptionDialog(initEx);
				return;
			}

			// Ensure we have a MainForm, assumes null is to cancel running
			if (null == mainForm)
				return;

			// Assign the MainForm and Run the application...
			try
			{
				MessageService.MainForm = mainForm;
				this.mainFormData = AppLocalUserData.Get(mainForm.Name, new AppData());
				this.MainForm = mainForm;
				this.MainForm.Load += new EventHandler(HandleLoad);
				this.MainForm.FormClosed += new FormClosedEventHandler(HandleClosed);
				Application.Run(this);
			}
			catch (Exception initEx)
			{
				ShowAppExceptionDialog(initEx);
				return;
			}
		}
		/// <summary>
		/// Update the status and the progressbar
		/// </summary>
		/// <param name="message">The Status message</param>
		/// <param name="value">The value of the ProgressBar</param>
		public virtual void UpdateSplash(string message, int value)
		{
			if (null == this.MainForm)
				return;
			this._statusInfo = message;
			this._progress = value;
			this.ChangeProgressBar();
			this.ChangeStatusText();
			Application.DoEvents();
		}
		/// <summary>
		/// Update the progressbar
		/// </summary>
		/// <param name="value">The value of the ProgressBar</param>
		public virtual void UpdateSplash(int value)
		{
			if (null == this.MainForm)
				return;
			this._progress = value;
			this.ChangeProgressBar();
			Application.DoEvents();
		}
		/// <summary>
		/// Update the status and the progressbar
		/// </summary>
		/// <param name="message">The Status message</param>
		public virtual void UpdateSplash(string message)
		{
			if (null == this.MainForm)
				return;
			this._statusInfo = message;
			this.ChangeStatusText();
			Application.DoEvents();
		}

		/// <summary>
		/// Is this the First Run of this application for the specified Scope (Machine or User)
		/// </summary>
		/// <remarks>
		/// IsFirstUse, checks to see if the first-run flag has been set. If it hasn't, then it sets it.
		/// </remarks>
		/// <param name="scope">Specifies the Scope (Machine or User) when validating the first-run flag</param>
		/// <returns>true if this is the first run for the requested scope</returns>
		public bool IsFirstUse(FirstRunScope scope)
		{
			return IsFirstUse(scope, false);
		}
		/// <summary>
		/// Is this the First Run of this application for the specified Scope (Machine or User)
		/// </summary>
		/// <remarks>
		/// IsFirstUse, checks to see if the first-run flag has been set. If it 
		/// hasn't and <paramref name="EvaluateOnly"/> is NOT set to true, then it sets it.
		/// </remarks>
		/// <param name="scope">Specifies the Scope (Machine or User) when validating the first-run flag</param>
		/// <param name="EvaluateOnly">If true, prevents the first-run flag from being set</param>
		/// <returns>true if this is the first run for the requested scope</returns>
		public bool IsFirstUse(FirstRunScope scope, bool EvaluateOnly)
		{
			string now = DateTime.Now.ToFileTime().ToString();
			if (scope == FirstRunScope.User)
			{
				string firstRun = (string)Application.UserAppDataRegistry.GetValue("FirstRun", string.Empty);
				if (string.IsNullOrEmpty(firstRun))
				{
					// First Run...
					if (!EvaluateOnly)
						Application.UserAppDataRegistry.SetValue("FirstRun", now);
					return true;
				}
			}
			else
			{
				string firstRun = (string)Application.CommonAppDataRegistry.GetValue("FirstRun", string.Empty);
				if (string.IsNullOrEmpty(firstRun))
				{
					// First Run...
					if (!EvaluateOnly)
						Application.CommonAppDataRegistry.SetValue("FirstRun", now);
					return true;
				}
			}
			return false;
		}

		/// <summary>
		/// Is this the First Run of this application for this user on this machine.
		/// </summary>
		/// <remarks>
		/// The check to see if the first run flag has been set. If not it sets it.
		/// </remarks>
		/// <returns>True if this is the first run</returns>
		[Obsolete("This method is now obsolete. Please use IsFirstUse(FirstRunScope scope);", false)]
		public bool FirstRunUser()
		{
			return this.FirstRunUser(false);
		}
		/// <summary>
		/// Is this the First Run of this application for this user on this machine.
		/// </summary>
		/// <remarks>
		/// The check to see if the first run flag has been set. If not it sets it unless EvaluateOnly is true.
		/// </remarks>
		/// <param name="EvaluateOnly">If true, prevents the FirstRun flag from being set</param>
		/// <returns>True if this is the first run</returns>
		[Obsolete("This method is now obsolete. Please use IsFirstUse(FirstRunScope scope);", false)]
		public bool FirstRunUser(bool EvaluateOnly)
		{
			string now = DateTime.Now.ToFileTime().ToString();
			string firstRun = (string)Application.UserAppDataRegistry.GetValue("FirstRun", string.Empty);
			if (string.IsNullOrEmpty(firstRun))
			{
				// First Run...
				if (!EvaluateOnly)
					Application.UserAppDataRegistry.SetValue("FirstRun", now);
				return true;
			}
			return false;
		}
		/// <summary>
		/// Is this the First Run of this application on this machine.
		/// </summary>
		/// <remarks>
		/// The check to see if the first run flag has been set. If not it sets it.
		/// </remarks>
		/// <returns>True if this is the first run</returns>
		[Obsolete("This method is now obsolete. Please use IsFirstUse(FirstRunScope scope);", false)]
		public bool FirstRunMachine()
		{
			return this.FirstRunMachine(false);
		}
		/// <summary>
		/// Is this the First Run of this application on this machine.
		/// </summary>
		/// <remarks>
		/// The check to see if the first run flag has been set. If not it sets it unless EvaluateOnly is true.
		/// </remarks>
		/// <param name="EvaluateOnly">If true, prevents the FirstRun flag from being set</param>
		/// <returns>True if this is the first run</returns>
		[Obsolete("This method is now obsolete. Please use IsFirstUse(FirstRunScope scope);", false)]
		public bool FirstRunMachine(bool EvaluateOnly)
		{
			string now = DateTime.Now.ToFileTime().ToString();
			string firstRun = (string)Application.CommonAppDataRegistry.GetValue("FirstRun", string.Empty);
			if (string.IsNullOrEmpty(firstRun))
			{
				// First Run...
				if (!EvaluateOnly)
					Application.CommonAppDataRegistry.SetValue("FirstRun", now);
				return true;
			}
			return false;
		}

		/// <summary>
		/// Internally used as a thread function - showing the form and
		/// starting the messageloop for it
		/// </summary>
		private void ShowThread<TSplash>() where TSplash : Form, ISplashScreen
		{
			// Create the splash screen
			try { this.MainForm = Activator.CreateInstance<TSplash>(); }
			catch (Exception ex)
			{
				// Remember we had an exception
				initException = new InitializationException("Failed to load the SplashScreen.", ex);
				return;
			}
			finally
			{
				// Indicate we're done creating the splash screen and will begin running the form
				uievent.Set();
			}

			// Ensure no errors
			if (null != initException)
				return;

			// Run the splash screen
			Application.Run(this);

			// Indicate the splash screen has closed
			uievent.Set();
		}
		/// <summary>
		/// Closes the SplashScreen or MainForm
		/// </summary>
		private void Close()
		{
			if (this.MainForm == null) return;

			if (this.MainForm.InvokeRequired)
			{
				try { this.MainForm.Invoke(new MethodInvoker(Close)); }
				catch { }
				return;
			}
			this.MainForm.Close();
			this.MainForm = null;
		}
		/// <summary>
		/// Set the progress bar value
		/// </summary>
		private void ChangeProgressBar()
		{
			try
			{
				if (this.MainForm.InvokeRequired)
				{
					this.MainForm.Invoke(new MethodInvoker(this.ChangeProgressBar));
					return;
				}

				ISplashScreen f = (ISplashScreen)this.MainForm;
				f.Update(this._progress);
			}
			catch { }
		}
		/// <summary>
		/// Set the status text
		/// </summary>
		private void ChangeStatusText()
		{
			try
			{
				if (this.MainForm.InvokeRequired)
				{
					this.MainForm.Invoke(new MethodInvoker(this.ChangeStatusText));
					return;
				}

				ISplashScreen f = (ISplashScreen)this.MainForm;
				f.Update(this._statusInfo);
			}
			catch { }
		}

		/// <summary>
		/// Handles the form's Load event by restoring the form's location, size, and/or state from the <see cref="AppLocalUserData"/>.</summary>
		/// <param name="sender">The object who sent the event.</param>
		/// <param name="e">The event data.</param>
		/// <remarks>This handler just calls the <see cref="Restore" /> method.</remarks>
		private void HandleLoad(object sender, EventArgs e)
		{
			try
			{
				this.loading = true;
				this.Restore();
			}
			finally
			{
				this.loading = false;
			}
		}
		/// <summary>
		/// Handles the form's Closed event by saving the form's location, size, and state from the <see cref="AppLocalUserData"/>.</summary>
		/// <param name="sender">The object who sent the event.</param>
		/// <param name="e">The event data.</param>
		/// <remarks>This handler just calls the <see cref="Save" /> method.</remarks>
		private void HandleClosed(object sender, FormClosedEventArgs e)
		{
			this.Save();
		}
		/// <summary>
		/// Restores the form's location, size, and/or state from the <see cref="AppLocalUserData"/>.</summary>
		/// <remarks>To restore the form's placement, the <see cref="Form" /> must not be null. The aspects of 
		/// the placement that are restored are based on the values of the <see cref="RestoreLocation" />, 
		/// <see cref="RestoreSize" />, and <see cref="RestoreState" /> properties. 
		/// The location will not be restored if doing so would cause the form to fall outside 
		/// the bounds of the available screen(s). Also, if the form is initially minimized
		/// or maximized, its state will not be restored.</remarks>
		/// <seealso cref="Save" />
		public void Restore()
		{

			if (this.MainForm == null)
				return;

			if (RestoreState)
			{
				FormWindowState state = FormWindowState.Normal;
				state = (FormWindowState)mainFormData.Get("State", 0);
				switch (state)
				{
					case FormWindowState.Minimized:
						this.MainForm.WindowState = FormWindowState.Normal;
						break;

					case FormWindowState.Normal:
						if (RestoreLocation)
						{
							Point location = new Point(mainFormData.Get("X", this.MainForm.RestoreBounds.X), mainFormData.Get("Y", this.MainForm.RestoreBounds.Y));

							// Verify that the location falls inside one of the available screens,
							// otherwise don't restore it
							foreach (Screen screen in Screen.AllScreens)
							{
								if (!loading || screen.Bounds.Contains(location))
								{
									this.MainForm.Location = location;
									break;
								}
							}
						}
						if (RestoreSize)
							this.MainForm.Size = new Size(mainFormData.Get("Width", this.MainForm.RestoreBounds.Width), mainFormData.Get("Height", this.MainForm.RestoreBounds.Height));
						break;

					case FormWindowState.Maximized:
						this.MainForm.WindowState = FormWindowState.Maximized;
						break;
				}
			}
			else
			{
				if (RestoreLocation)
				{
					Point location = new Point(mainFormData.Get("X", 0), mainFormData.Get("Y", 0));

					// Verify that the location falls inside one of the available screens,
					// otherwise don't restore it
					foreach (Screen screen in Screen.AllScreens)
					{
						if (!loading || screen.Bounds.Contains(location))
						{
							this.MainForm.Location = location;
							break;
						}
					}
				}
				if (RestoreSize)
					this.MainForm.Size = new Size(mainFormData.Get("Width", 0), mainFormData.Get("Height", 0));
			}
		}
		/// <summary>
		/// Saves the form's location, size, and state to the <see cref="AppLocalUserData"/>.</summary>
		/// <remarks>
		/// To save the form's placement, the <see cref="System.Windows.Forms.ApplicationContext.MainForm"/> must not be null.</remarks>
		/// <seealso cref="Restore" />
		public void Save()
		{
			if (this.MainForm == null)
				return;

			mainFormData.Set("X", this.MainForm.Left.ToString());
			mainFormData.Set("Y", this.MainForm.Top.ToString());
			mainFormData.Set("Width", this.MainForm.Width.ToString());
			mainFormData.Set("Height", this.MainForm.Height.ToString());
			mainFormData.Set("State", (int)this.MainForm.WindowState);
		}

		/// <summary>
		/// Raise Exception Dialog box for both UI and non-UI Unhandled Exceptions
		/// </summary>
		/// <param name="unhandledException">Catched exception</param>
		protected virtual void ShowAppExceptionDialog(Exception unhandledException)
		{
			System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();
			try { }
			finally
			{
				ShowAppExceptionDialog(unhandledException, false, false);
			}
		}
		/// <summary>
		/// Raise Exception Dialog box for both UI and non-UI Unhandled Exceptions
		/// </summary>
		/// <param name="unhandledException">Catched exception</param>
		/// <param name="allowRestart">Can the user choose to Restart</param>
		protected virtual void ShowAppExceptionDialog(Exception unhandledException, bool allowRestart)
		{
			System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();
			try { }
			finally
			{
				ShowAppExceptionDialog(unhandledException, allowRestart, true);
			}
		}
		/// <summary>
		/// Raise Exception Dialog box for both UI and non-UI Unhandled Exceptions
		/// </summary>
		/// <param name="unhandledException">Catched exception</param>
		/// <param name="allowRestart">Can the user choose to Restart</param>
		/// <param name="terminating">Is the application terminating</param>
		protected virtual void ShowAppExceptionDialog(Exception unhandledException, bool allowRestart, bool terminating)
		{
			Exception ex = unhandledException;
			bool doRestart = false;

			if (ex == null)
				ex = new Exception("Unknown unhandled Exception has occurred!");

			using (var exView = new AppExceptionDialog())
			{
				var appName = Application.ProductName;
				exView.Text = appName + " - Unhandled Exception";
				exView.lblTitle.Text = String.Format(exView.lblTitle.Text, appName);
				if (!terminating)
					exView.lblTitle.Text = exView.lblTitle.Text.Replace(" and needed to close", "");


				// Show/Hide restart CheckBox
				exView.chkRestart.Visible = allowRestart;
				if (allowRestart)
				{
					exView.chkRestart.Text = String.Format(exView.chkRestart.Text, appName);
					exView.chkRestart.CheckedChanged += delegate(object sender, EventArgs e)
					{
						doRestart = exView.chkRestart.Checked;
					};
				}

				// Disable the Send Button if SendException event is not handled
				if (null == SendException)
				{
					exView.btnSend.Visible = false;
					exView.btnNotSend.Text = "&OK";
					exView.lblViewReport.Visible = false;
					exView.lklViewReport.Visible = false;
					exView.lblCaption.Text = "Description:";
					exView.lblDescription.Text = ex.Message;
				}
				else
				{
					// Do not show Click-Here link if ShowError is not handled
					if (null == ShowError)
					{
						exView.lblViewReport.Visible = false;
						exView.lklViewReport.Visible = false;
						exView.lblCaption.Text = "Description:";
						exView.lblDescription.Text = ex.Message;
					}
					else
					{
						exView.lblViewReport.Visible = true;
						exView.lklViewReport.Visible = true;
						// Handle clicks on view report link
						exView.lklViewReport.LinkClicked += delegate(object o, LinkLabelLinkClickedEventArgs ev)
						{
							if (ShowError != null)
							{
								SendExceptionEventArgs ar = new SendExceptionEventArgs(true, ex, doRestart);
								ShowError(this, ar);
							}
						};
					}

				}

				// Show the Exception...
				bool sendDetails = (exView.ShowDialog() == System.Windows.Forms.DialogResult.Yes);

				// Send to Author/Support Desk
				if (SendException != null)
				{
					SendExceptionEventArgs ar = new SendExceptionEventArgs(sendDetails, ex, doRestart);
					SendException(this, ar);
				}
			}
		}

		#endregion

		#region Event Handlers
		void Application_ApplicationExit(object sender, EventArgs e)
		{
			try { AppLocalUserData.Save(); }
			catch { }
			try { AppUserData.Save(); }
			catch { }
			try { AppCommonData.Save(); }
			catch { }
		}
		void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
		{
			if (Application.MessageLoop)
			{
#if !DEBUG
				HSS.Logging.Log.WriteException(e.Exception, Environment.UserName, "Thread Exception");
#endif
				this.ShowAppExceptionDialog(e.Exception);
			}
		}
		void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
		{
			if (Application.MessageLoop)
			{
				Exception fatal = (Exception)e.ExceptionObject;
				Console.WriteLine(fatal.ToString());
				if (!System.Diagnostics.Debugger.IsAttached)
				{
					HSS.Logging.Log.WriteException(fatal, Environment.UserName, "Unhandled Exception");
					this.ShowAppExceptionDialog(fatal, false, e.IsTerminating);
				}
			}
		}
		#endregion
	}
	#endregion
}